// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
// { dg-do compile { target c++26 } }
// { dg-require-effective-target exceptions_enabled }

struct A {
  explicit constexpr A (int x) noexcept : a (x) {}
  constexpr virtual int foo () const noexcept { return a; }
  constexpr virtual ~A () {}
  int a;
};
struct B : public A {
  explicit constexpr B (int x) noexcept : A (x) {}
  constexpr int foo () const noexcept override { return a | 0x10; }
};
struct C : public A {
  explicit constexpr C (int x) noexcept : A (x) {}
};
struct D : public A {
  explicit constexpr D (int x) noexcept : A (x) {}
};
struct E {
  constexpr E () noexcept : e (0) {}
  explicit constexpr E (int x) noexcept : e (x) {}
  int e;
};
struct F : public E, public B {
  explicit constexpr F (int x) noexcept : B (x) {}
};
struct G : public E, public C {
  explicit constexpr G (int x) noexcept : C (x) {}
};
struct H : public E, public D {
  explicit constexpr H (int x) noexcept : D (x) {}
};

consteval int
bar (void (*fn) ())
{
  int r = 0;
  try
    {
      fn ();
    }
  catch (C *a)
    {
      r = a->foo () | 0x20;
      delete a;
    }
  catch (const C *b)
    {
      r = b->foo () | 0x60;
      delete b;
    }
  catch (A *c)
    {
      r = c->foo ();
      delete c;
    }
  catch (const A *d)
    {
      r = d->foo () | 0x40;
      delete d;
    }
  return r;
}

static_assert (bar ([] { throw new A { 1 }; }) == 1);
static_assert (bar ([] { throw new B { 2 }; }) == 0x12);
static_assert (bar ([] { throw new C { 3 }; }) == 0x23);
static_assert (bar ([] { throw new D { 4 }; }) == 4);
constexpr int a = bar ([] { throw new E { 5 }; });	// { dg-error "uncaught exception of type 'E\\\*'" }
static_assert (bar ([] { throw new F { 6 }; }) == 0x16);
static_assert (bar ([] { throw new G { 7 }; }) == 0x27);
static_assert (bar ([] { throw new H { 8 }; }) == 8);
